import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { FormBottonsConfigService } from '../services';
import { EditState } from './contracts';
import { EntityStateService } from './entity-detail.state.service';

@Component({
  selector: '',
  template: ``,
})
export abstract class EntityDetailComponent<T extends { id: string }> implements OnInit {
  public validateForm: FormGroup;
  searchText: string;

  constructor(
    public fb: FormBuilder,
    public buttonConfigService: FormBottonsConfigService,
    public stateService: EntityStateService<T>
  ) {}

  ngOnInit(): void {
    this.initValidateForm();

    this.validateForm.valueChanges.subscribe(data => this.onFormValueChanged(data));
    this.buttonConfigService.pageButtonGroup.visable = false;

    this.buttonConfigService.onSaveClick.subscribe(() => this.submitForm());
    this.buttonConfigService.onGiveupClick.subscribe(() => this.giveUp());
    this.buttonConfigService.onNewClick.subscribe(() => this.changeToEmpty());
    this.buttonConfigService.onCopyClick.subscribe(() => this.copyToCreating());
    this.buttonConfigService.onDeleteClick.subscribe(() => this.delete());

    this.buttonConfigService.onFindClick.subscribe(() => this.select());
    this.buttonConfigService.onListClick.subscribe(() => this.navigateToList());

    this.stateService.onStateChanged.subscribe(editState => {
      this.buttonConfigService.changeState(editState);
    });

    this.stateService.onCurrentModelChanged.subscribe(model => {
      if (this.stateService.state == EditState.Empty) {
        this.resetFormValue();
      } else if (this.stateService.state == EditState.View) {
        this.setFormValue(model);
      }
    });

    this.afterInit();
  }

  public afterInit() {}

  public giveUp() {
    if (this.stateService.state == EditState.Creating) {
      this.stateService.giveupToEmpty();
    } else if (this.stateService.state == EditState.Editing) {
      this.stateService.giveupToView();
    }
  }

  public changeToEmpty() {
    this.stateService.changeToEmpty();
  }

  public copyToCreating() {
    this.stateService.copyToCreating().then(model => this.setFormValue(model));
  }

  public delete() {
    this.stateService.delete().then(() => this.resetFormValue());
  }

  public select() {
    this.refSelectModels().then(models => {
      if (!!models?.length) {
        this.stateService.changeToView(models[0].id);
      }
    });
  }

  public search() {
    if (!this.searchText) return;
    this.stateService
      .search(this.searchText)
      .then(model => {
        this.stateService.changeToView(model.id);
      })
      .catch(() => {});
  }

  public navigateToList() {
    this.stateService.navigateToList();
  }

  public onFormValueChanged(data: any) {
    switch (this.stateService.state) {
      case EditState.Empty:
        for (const [key, val] of Object.entries(data)) {
          if (!val) continue;
          this.stateService.changeToCreating();
          break;
        }
        break;

      case EditState.View:
        for (const [key, val] of Object.entries(data)) {
          if (!this.stateService.currentModel[key]) continue;
          if (!val && !this.stateService.currentModel[key]) continue;
          if (val == this.stateService.currentModel[key]) continue;
          this.stateService.changeToEditing();
          break;
        }
        break;
      default:
        break;
    }
  }

  public submitForm() {
    this.stateService.controlValidate(!this.validateForm.valid);
    if (this.stateService.state == EditState.Creating) {
      this.stateService.save({
        ...this.validateForm.value,
        lockoutEnabled: !!this.validateForm.value?.lockoutEnabled,
        ...this.getEntity(),
      });
    } else if (this.stateService.state == EditState.Editing) {
      this.stateService.edit({
        ...this.stateService.currentModel,
        ...this.validateForm.value,
        lockoutEnabled: !!this.validateForm.value?.lockoutEnabled,
        id: this.stateService.currentModel.id,
        ...this.getEntity(),
      });
    }
  }

  protected getEntity() {
    return {};
  }

  public resetFormValue() {
    this.validateForm?.reset({}, { emitEvent: false });
    this.updateFormValidity();
  }

  public setFormValue(model: T) {
    this.validateForm.patchValue(model, { emitEvent: false });
    this.updateFormValidity();
  }

  protected updateFormValidity() {
    for (const key in this.validateForm.controls) {
      this.validateForm.controls[key].markAsPristine();
      this.validateForm.controls[key].markAsUntouched();
      this.validateForm.controls[key].updateValueAndValidity();
    }
  }

  public abstract initValidateForm();
  public abstract refSelectModels(): Promise<T[]>;
}
